﻿using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Key = System.Windows.Input.Key;
using Keyboard = System.Windows.Input.Keyboard;

namespace RomBotUI.Map
{
    public class ZoomBorder : Border
    {
        private UIElement child = null;
        private Point origin;
        private Point start;

        private TranslateTransform GetTranslateTransform( UIElement element )
        {
            return ( TranslateTransform ) ( ( TransformGroup ) element.RenderTransform )
              .Children.First( tr => tr is TranslateTransform );
        }

        private ScaleTransform GetScaleTransform( UIElement element )
        {
            return ( ScaleTransform ) ( ( TransformGroup ) element.RenderTransform )
              .Children.First( tr => tr is ScaleTransform );
        }

        internal ScaleTransform ScaleTransform
        {
            get { return GetScaleTransform( Child ); }
        }

        internal TranslateTransform TranslateTransform
        {
            get { return GetTranslateTransform( Child ); }
        }

        public static readonly DependencyProperty ScaleProperty =
          DependencyProperty.Register( "Scale", typeof( double ),
          typeof( ZoomBorder ), new UIPropertyMetadata( 1d ) );

        public double Scale
        {
            get { return ( double ) GetValue( ScaleProperty ); }
            set
            {
                SetValue( ScaleProperty, value );
                SetScale( value );
            }
        }

        public override UIElement Child
        {
            get { return base.Child; }
            set
            {
                if ( value != null && value != this.Child )
                    this.Initialize( value );
                base.Child = value;
            }
        }

        public void Initialize( UIElement element )
        {
            this.child = element;
            if ( child != null )
            {
                var group = new TransformGroup();
                var st = new ScaleTransform();
                group.Children.Add( st );
                var tt = new TranslateTransform();
                group.Children.Add( tt );
                child.RenderTransform = group;

                child.RenderTransformOrigin = new Point( 0.0, 0.0 );
                this.MouseWheel += child_MouseWheel;
                this.MouseLeftButtonDown += child_MouseLeftButtonDown;
                this.MouseLeftButtonUp += child_MouseLeftButtonUp;
                this.MouseMove += child_MouseMove;
                this.PreviewMouseRightButtonDown += this.child_PreviewMouseRightButtonDown;
            }
        }

        public void Reset()
        {
            if ( child != null )
            {
                // reset zoom
                var st = GetScaleTransform( child );
                st.ScaleX = 1.0;
                st.ScaleY = 1.0;

                SetValue( ScaleProperty, 1.0 );

                // reset pan
                var tt = GetTranslateTransform( child );
                tt.X = 0.5;
                tt.Y = 0.5;
            }
        }

        #region Child Events

        private void child_MouseWheel( object sender, MouseWheelEventArgs e )
        {
            if ( child != null )
            {
                var st = GetScaleTransform( child );
                var tt = GetTranslateTransform( child );

                var zoom = e.Delta > 0 ? .2 : -.2;
                if ( !( e.Delta > 0 ) && ( st.ScaleX < .4 || st.ScaleY < .4 ) )
                    return;

                var relative = e.GetPosition( child );

                var abosuluteX = relative.X * st.ScaleX + tt.X;
                var abosuluteY = relative.Y * st.ScaleY + tt.Y;

                st.ScaleX += zoom;
                st.ScaleY += zoom;

                SetValue( ScaleProperty, st.ScaleX );
                //Scale = st.ScaleX;

                tt.X = abosuluteX - relative.X * st.ScaleX;
                tt.Y = abosuluteY - relative.Y * st.ScaleY;
            }
        }

        private void SetScale( double scale )
        {
            if ( child != null )
            {
                var st = GetScaleTransform( child );
                var tt = GetTranslateTransform( child );

                var relative = TranslatePoint( ( ( Map ) child ).PlayerPoint, child );
                var abosuluteX = relative.X * st.ScaleX + tt.X;
                var abosuluteY = relative.Y * st.ScaleY + tt.Y;

                st.ScaleX = scale;
                st.ScaleY = scale;

                tt.X = abosuluteX - relative.X * st.ScaleX;
                tt.Y = abosuluteY - relative.Y * st.ScaleY;
            }
        }

        private void child_MouseLeftButtonDown( object sender, MouseButtonEventArgs e )
        {
            if ( !Keyboard.IsKeyDown( Key.LeftCtrl ) && !Keyboard.IsKeyDown( Key.RightCtrl ) )
            {
                return;
            }
            if ( child != null )
            {
                var tt = GetTranslateTransform( child );
                start = e.GetPosition( this );
                origin = new Point( tt.X, tt.Y );
                this.Cursor = Cursors.Hand;
                child.CaptureMouse();
            }
        }

        private void child_MouseLeftButtonUp( object sender, MouseButtonEventArgs e )
        {
            if ( child != null )
            {
                child.ReleaseMouseCapture();
                this.Cursor = Cursors.Arrow;
            }
        }

        void child_PreviewMouseRightButtonDown( object sender, MouseButtonEventArgs e )
        {
            this.Reset();
        }

        private void child_MouseMove( object sender, MouseEventArgs e )
        {
            if ( child != null )
            {
                if ( child.IsMouseCaptured )
                {
                    var tt = GetTranslateTransform( child );
                    Vector v = start - e.GetPosition( this );
                    tt.X = origin.X - v.X;
                    tt.Y = origin.Y - v.Y;
                }
            }
        }

        #endregion
    }
}